package com.godfrey.poi.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.godfrey.poi.pojo.District;
import com.godfrey.poi.pojo.Poi;
import com.godfrey.poi.pojo.RectangleCoordinate;
import com.godfrey.poi.service.PoiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author godfrey
 * @since 2021-12-05
 */
@Service
@Slf4j
public class PoiServiceImpl implements PoiService {

    private final RestTemplate restTemplate;

    @Autowired
    public PoiServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * poi数据
     */
    private List<Poi> poisData = null;

    /**
     * 导出路径
     */
    private final String PATH = System.getProperty("user.dir").replace("gaode_poi", "");

    /**
     * 导出文件名
     */
    StringBuilder fileName = null;

    @Override
    public List<District> searchNextDistrict(String adcode) {
        //设置请求参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("keywords", adcode);

        JSONObject districts = postForJson("config/district", params);
        //获取下一级行政区
        JSONArray districtsArrary = districts.getJSONArray("districts").getJSONObject(0).getJSONArray("districts");

        //JSONArray转为List
        return JSONObject.parseArray(districtsArrary.toJSONString(), District.class);
    }

    @Override
    public String searchCoordinates(String adcode) {
        //设置请求参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("keywords", adcode);
        params.add("subdistrict", "0");
        params.add("extensions", "all");

        //查询边界坐标
        JSONObject districts = postForJson("config/district", params);
        //获取边界坐标
        return districts.getJSONArray("districts").getJSONObject(0).get("polyline").toString();
    }

    @Override
    public List<Poi> searchPoi(String coordinates, District district, String keywords, String categoryCode) {
        //获取行政区边界范围矩形
        final RectangleCoordinate coordinate;
        if ("street".equals(district.getLevel())) {
            coordinate = transferRectangleCoordinate(coordinates);
        } else {
            coordinate = getRectangle(coordinates);
        }
        //切割矩形
        List<RectangleCoordinate> validRectangle = validRectangle(coordinate, keywords, categoryCode);
        log.info("获取到所有切割后的矩形， 个数为：[" + validRectangle.size() + "]");
        List<Poi> poisList = new ArrayList<>();

        validRectangle.forEach(e -> {
            //对每个小矩形爬取,并添加到list
            poisList.addAll(startCrawl(e, keywords, categoryCode));
        });

        poisData = new ArrayList<>();
        fileName = new StringBuilder();
        //去重
        if ("street".equals(district.getLevel())) {
            //TODO:乡镇过滤(待完善)
            poisData.addAll(poisList);
            if (!poisData.isEmpty()) {
                fileName.append(poisData.get(0).getPname()).append(poisData.get(0).getCityname()).append(poisData.get(0).getAdname()).append(district.getName()).append("POI");
            }
        } else if ("district".equals(district.getLevel())) {
            poisData = poisList.stream().filter(p -> p.getAdname().equals(district.getName()))
                    .collect(Collectors.toList());
            if (!poisData.isEmpty()) {
                fileName.append(poisData.get(0).getPname()).append(poisData.get(0).getCityname()).append(poisData.get(0).getAdname()).append("POI");
            }
        } else if ("city".equals(district.getLevel())) {
            poisData = poisList.stream().filter(p -> p.getCityname().equals(district.getName()))
                    .collect(Collectors.toList());
            if (!poisData.isEmpty()) {
                fileName.append(poisData.get(0).getPname()).append(poisData.get(0).getCityname()).append("POI");
            }
        } else if ("province".equals(district.getLevel())) {
            poisData = poisList.stream().filter(p -> p.getPname().equals(district.getName()))
                    .collect(Collectors.toList());
            if (!poisData.isEmpty()) {
                fileName.append(poisData.get(0).getPname()).append("POI");
            }
        }
        log.info(keywords + "POI个数：" + poisData.size());
        return poisData;
    }

    /**
     * 获取行政区边界范围矩形
     *
     * @param coordinates 行政区边界坐标
     * @return com.godfrey.poi.pojo.RectangleCoordinate
     */
    private RectangleCoordinate getRectangle(String coordinates) {
        coordinates = coordinates.replace("|", ";");
        String[] split = coordinates.split(";");
        //经度
        List<Double> longitude = new ArrayList<>();
        //维度
        List<Double> latitude = new ArrayList<>();
        Arrays.stream(split).forEach(e -> {
            String[] s = e.split(",");
            longitude.add(Double.parseDouble(s[0]));
            latitude.add(Double.parseDouble(s[1]));
        });
        //获得矩形区域
        return new RectangleCoordinate(Collections.min(longitude), Collections.max(latitude), Collections.max(longitude), Collections.min(latitude));
    }

    /**
     * String转为乡镇矩形边界
     *
     * @param coordinates 边界字符串
     * @return com.godfrey.poi.pojo.RectangleCoordinate
     */
    private RectangleCoordinate transferRectangleCoordinate(String coordinates) {
        final RectangleCoordinate coordinate;
        String x0 = coordinates.substring(0, coordinates.indexOf(","));
        String y0 = coordinates.substring(coordinates.indexOf(",") + 1, coordinates.indexOf(";"));
        String x1 = coordinates.substring(coordinates.indexOf(";") + 1, coordinates.lastIndexOf(","));
        String y1 = coordinates.substring(coordinates.lastIndexOf(",") + 1);
        return new RectangleCoordinate(x0, y0, x1, y1);
    }

    /**
     * 获取矩形块中符合调用api的小矩形块的集合,设定阈值800
     *
     * @param coordinate   行政区边界坐标
     * @param categoryCode POI行业分类编码
     * @return java.util.List<com.godfrey.poi.pojo.RectangleCoordinate>
     */
    private List<RectangleCoordinate> validRectangle(RectangleCoordinate coordinate, String keywords, String categoryCode) {
        //设置请求参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("polygon", coordinate.getPolygonString());
        params.add("keywords", keywords);
        params.add("types", categoryCode);
        JSONObject districtsPois = postForJson("place/polygon", params);
        //小矩形区域
        List<RectangleCoordinate> validCoordinate = new ArrayList<>();
        if (districtsPois.getIntValue("count") >= 800) {
            List<RectangleCoordinate> splitRectangleList = getSplitRectangleList(coordinate);
            splitRectangleList.forEach(e -> validCoordinate.addAll(validRectangle(e, keywords, categoryCode)));
        } else {
            log.info("添加小矩形： " + coordinate.toString());
            validCoordinate.add(coordinate);
        }
        return validCoordinate;
    }

    /**
     * 将矩形4等分成小矩形 然后返回4个 小矩形的经纬度集合
     *
     * @param coordinate POI行业分类编码
     * @return java.util.List<com.godfrey.poi.pojo.RectangleCoordinate>
     */
    private List<RectangleCoordinate> getSplitRectangleList(RectangleCoordinate coordinate) {
        List<RectangleCoordinate> splitRectangleList = new ArrayList<>();
        splitRectangleList.add(new RectangleCoordinate(coordinate.getX0(), coordinate.getY0(), coordinate.getAverageX(), coordinate.getAverageY()));
        splitRectangleList.add(new RectangleCoordinate(coordinate.getAverageX(), coordinate.getY0(), coordinate.getX1(), coordinate.getAverageY()));
        splitRectangleList.add(new RectangleCoordinate(coordinate.getX0(), coordinate.getAverageY(), coordinate.getAverageX(), coordinate.getY1()));
        splitRectangleList.add(new RectangleCoordinate(coordinate.getAverageX(), coordinate.getAverageY(), coordinate.getX1(), coordinate.getY1()));
        return splitRectangleList;
    }

    /**
     * 开始爬取所有POI数据
     *
     * @param coordinate   边界矩形坐标
     * @param keywords     关键词
     * @param categoryCode POI行业分类编码
     * @return java.util.List<com.godfrey.poi.pojo.District>
     */
    private List<Poi> startCrawl(RectangleCoordinate coordinate, String keywords, String categoryCode) {
        //当前爬取的索引
        int pageNum = 1;
        //每页抓取的个数
        final String offset = "50";
        //当前爬取内容是否是最后一页
        boolean isLastPage = false;
        //每次查询结果
        JSONObject searchResult;
        //数据集
        List<Poi> datas = new ArrayList<>();

        //设置请求参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("polygon", coordinate.getPolygonString());
        params.add("offset", offset);
        params.add("keywords", keywords);
        params.add("types", categoryCode);
        params.add("extensions", "all");
        params.add("page", Integer.toString(pageNum));

        log.info("准备爬取矩形：" + coordinate.toString());
        while (!isLastPage) {
            params.set("page", Integer.toString(pageNum));
            JSONObject districtsPois = postForJson("place/polygon", params);
            log.info("爬取页数：" + pageNum);
            List<Poi> data = districtsPois.getJSONArray("pois").toJavaList(Poi.class);
            datas.addAll(data);
            if (data.size() < 50) {
                isLastPage = true;
                log.info("爬取完一个矩形，POI个数为：" + datas.size() + " ;当前页数为：" + pageNum);
            }
            ++pageNum;
        }
        return datas;
    }

    /**
     * 高德Api请求封装
     *
     * @param type   请求类型
     * @param params 请求参数
     * @return com.alibaba.fastjson.JSONObject
     */
    private JSONObject postForJson(String type, MultiValueMap<String, String> params) {
        //设置请求url
        String url = UriComponentsBuilder.fromUriString("https://lbs.amap.com/service/api/restapi")
                .queryParams(params)
                .build()
                .encode()
                .toString();
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        //设置请求体参数
        MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("type", type);
        multiValueMap.add("version", "v3");
        HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new HttpEntity<>(multiValueMap, headers);

        return restTemplate.postForObject(url, multiValueMapHttpEntity, JSONObject.class);
    }

    /**
     * 导出Poi
     */
    @Override
    public void exportPoi() {
        if (null != fileName) {
            EasyExcel.write(PATH + fileName + System.currentTimeMillis() + ".xlsx", Poi.class).sheet(fileName.toString()).doWrite(poisData);
            log.info("导出数据成功");
        }
    }
}